package com.mystnihon.demoftp.ftp;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.SocketException;
import java.util.EventListener;
import java.util.HashMap;

import my.swing.event.EventListenerLists;

import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import org.apache.commons.net.io.CopyStreamAdapter;
import org.apache.commons.net.io.CopyStreamEvent;
import org.apache.commons.net.io.Util;

import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;

public class FTPManager implements FTPInterface {

    public static final int Connected = 0, ConnectionFailed = 1, AuthentificationSucceed = 2, AuthentificationFailed = 3, DownloadSuccess = 4, DownloadFailed = 5, FileList = 6, NotDisconnected = 7,
            Disconnected = 8;

    private static final String TAG = FTPManager.class.getSimpleName();

    private boolean mIsConnected = false;
    private EventListenerLists mListeners;
    private FTPClient mFtpClient;
    private Context mContext;
    private HashMap<String, String> targetFiles = null;
    private HashMap<String, String> failedFiles = null;

    public FTPManager(Context context) {
        mContext = context;
        mListeners = new EventListenerLists();
        mFtpClient = new FTPClient();

    }

    public void connect(final String host, final int port) {

        if (!mIsConnected) {
            new AsyncTask<Void, Void, Boolean>() {

                @Override
                protected Boolean doInBackground(Void... params) {

                    try {
                        mFtpClient.connect(host, port);
                        // After connection attempt, you should check the reply code to verify
                        // success.
                        int reply = mFtpClient.getReplyCode();

                        if (!FTPReply.isPositiveCompletion(reply)) {
                            mFtpClient.disconnect();
                            Log.w(TAG, "FTP server refused connection.");
                            return false;
                        }
                        return true;
                    } catch (SocketException e) {
                        e.printStackTrace();
                        return false;
                    } catch (IOException e) {
                        e.printStackTrace();
                        return false;
                    }

                }

                protected void onPostExecute(Boolean result) {
                    super.onPostExecute(result);
                    fireConnect(result.booleanValue());
                };

            }.execute();
        } else {
            fireConnect(true);
        }
    }

    public void disconnect() {
        Log.i(TAG, "Disconnect");
        new AsyncTask<Void, Void, Void>() {
            @Override
            protected Void doInBackground(Void... params) {
                try {
                    if (mFtpClient.isConnected()) {
                        mFtpClient.abort();
                        mFtpClient.logout();
                        mFtpClient.disconnect();
                    }
                } catch (IOException e) {
                    Log.w(TAG, "[doInBackground] Exception", e);
                }
                return null;
            }

            protected void onPostExecute(Void result) {
                super.onPostExecute(result);
                fireDisconnect();

            };
        }.execute();
    }

    @Override
    public void download(final String ftpAddress, final File localFile) {

        new AsyncTask<Void, Void, Boolean>() {

            @Override
            protected Boolean doInBackground(Void... params) {
                try {

                    final FileOutputStream out = new FileOutputStream(localFile);
                    FTPFile[] files = mFtpClient.listFiles(ftpAddress);
                    long size = CopyStreamEvent.UNKNOWN_STREAM_SIZE;
                    if(files.length>0){
                        size = files[0].getSize();
                    }
                    
                    InputStream in = mFtpClient.retrieveFileStream(ftpAddress);
                    
                    Util.copyStream(in, out, mFtpClient.getBufferSize(), size, new CopyStreamAdapter() {
                        @Override
                        public void bytesTransferred(long totalBytesTransferred, int bytesTransferred, long streamSize) {
                            super.bytesTransferred(totalBytesTransferred, bytesTransferred, streamSize);
                            System.out.println("transferred: " + totalBytesTransferred + "| streamSize: "+streamSize);
                            fireDownloadProgress(localFile.getName(), totalBytesTransferred, -1);
                        }
                    });
                    in.close();
                    out.flush();
                    out.close();
                    if (!mFtpClient.completePendingCommand()) {
                        mFtpClient.logout();
                        mFtpClient.disconnect();
                        in.close();
                        out.close();
                        return false;
                    }
                    return true;
                } catch (IOException e) {
                    Log.w(TAG, "[doInBackground] Exception", e);
                    return false;
                }
            }

            protected void onPostExecute(Boolean result) {
                super.onPostExecute(result);
                fireDownload(ftpAddress, result);
            }

        }.execute();
    }

    public boolean isConnected() {
        return mFtpClient.isConnected();
    }

    @Override
    public void listDirectories() {
        try {
            FTPFile[] file = mFtpClient.listDirectories();
            fireDirectory(file, null);
        } catch (IOException e) {
            Log.w(TAG, "[doInBackground] Exception", e);

        }

    }

    @Override
    public void listDirectories(String directory) {
        try {
            FTPFile[] file = mFtpClient.listDirectories(directory);
            fireDirectory(file, directory);
        } catch (IOException e) {
            // TODO Auto-generated catch block

        }

    }

    @Override
    public void listFiles() {
        Log.i(TAG, "List files");

        new AsyncTask<Void, Void, FTPFile[]>() {

            @Override
            protected FTPFile[] doInBackground(Void... params) {
                try {
                    Log.i(TAG, "[doInBackground] will list files");
                    FTPFile[] values1 = mFtpClient.listDirectories();
                    FTPFile[] values2 = mFtpClient.listFiles();
                    if (values2 != null && values2.length > 0) {
                        Log.i(TAG, "[doInBackground] listFiles returned files");
                    }
                    FTPFile[] values = new FTPFile[values1.length + values2.length];
                    System.arraycopy(values1, 0, values, 0, values1.length);
                    System.arraycopy(values2, 0, values, values1.length, values2.length);
                    return values;

                } catch (IOException e) {
                    Log.w(TAG, "[doInBackground] Exception", e);
                    return null;
                }
            }

            protected void onPostExecute(FTPFile[] result) {
                super.onPostExecute(result);
                if (result != null) {
                    Log.i(TAG, "We have files");
                }
                Log.i(TAG, "fire files");
                fireFiles(result, null);
            };
        }.execute();

    }

    @Override
    public void listFiles(final String directory) {

        new AsyncTask<Void, Void, FTPFile[]>() {

            @Override
            protected FTPFile[] doInBackground(Void... params) {
                try {
                    Log.i(TAG, "[doInBackground] will list files");
                    FTPFile[] values1 = mFtpClient.listDirectories(directory);
                    FTPFile[] values2 = mFtpClient.listFiles(directory);
                    if (values2 != null && values2.length > 0) {
                        Log.i(TAG, "[doInBackground] listFiles returned files");
                    }
                    FTPFile[] values = new FTPFile[values1.length + values2.length];
                    System.arraycopy(values1, 0, values, 0, values1.length);
                    System.arraycopy(values2, 0, values, values1.length, values2.length);
                    return values;

                } catch (IOException e) {
                    Log.w(TAG, "[doInBackground] Exception", e);
                    return null;
                }
            }

            protected void onPostExecute(FTPFile[] result) {
                super.onPostExecute(result);
                if (result != null) {
                    Log.i(TAG, "We have files");
                }
                Log.i(TAG, "fire files");
                fireFiles(result, null);
            };
        }.execute();

    }

    @Override
    public void login(final String login, final String password) {
        new AsyncTask<String, Void, Boolean>() {
            @Override
            protected Boolean doInBackground(String... params) {
                try {
                    return mFtpClient.login(login, password);
                } catch (IOException e) {
                    return false;
                }

            }

            protected void onPostExecute(Boolean result) {
                super.onPostExecute(result);
                fireAuthenticated(result.booleanValue());
            };
        }.execute(login, password);
    }

    public void setFTPListener(FTPListener listener) {
        mListeners.add(FTPListener.class, listener);
    }

    private void fireAuthenticated(boolean logged) {
        for (FTPListener listener : getFTPListeners()) {
            listener.onAuthenticated(logged);
        }
    }

    private void fireConnect(boolean connected) {
        for (FTPListener listener : getFTPListeners()) {
            listener.onConnect(connected);
        }
    }

    private void fireDirectory(FTPFile[] files, String directory) {
        for (FTPListener listener : getFTPListeners()) {
            listener.onDirectories(files, directory);
        }
    }

    private void fireDisconnect() {
        for (FTPListener listener : getFTPListeners()) {
            listener.onDisconnected();
        }
    }

    private void fireFiles(FTPFile[] files, String directory) {
        for (FTPListener listener : getFTPListeners()) {
            listener.onFiles(files, directory);
        }
    }

    private void fireDownload(String file, Boolean result) {
        for (FTPListener listener : getFTPListeners()) {
            if (result) {
                Log.w(TAG, "File transfer succeed.");
                listener.onDownloadFinished(file, -1);
            } else {
                Log.w(TAG, "File transfer failed.");
                listener.onDownloadError(0, file, -1);
            }
        }
    };

    private void fireDownloadProgress(String file, long byteTransferred, int position) {
        for (FTPListener listener : getFTPListeners()) {
                listener.onDownloadProgress(file, byteTransferred, position);
        }
    };

    private FTPListener[] getFTPListeners() {
        return mListeners.getListeners(FTPListener.class);
    }

    public static interface FTPListener extends EventListener {
        public void onAuthenticated(boolean success);

        public void onConnect(boolean connected);

        public void onDirectories(FTPFile[] files, String directory);

        public void onDisconnected();

        public void onDownloadError(int errorCode, String downloadFailed, int downloadPos);

        public void onDownloadFinished(String file, int downloadPos);

        public void onDownloadProgress(String file, long byteTransferred, int downloadPos);

        public void onFiles(FTPFile[] files, String directory);
    }

    public void clearListeners() {
        mListeners.clearListeners();
    }

}
